/****************************************************************************
	在16通道读出版本中，采用芯片TMUX1208控制各个芯片的SPI片选信号，需要三个输入信号A0,A1,A2：
	A2		|	A1		|	A0		|	chip_name	|	CPOL	|	CPHA
	A2		|	A1		|	A0		|	chip_name	|	CPOL	|	CPHA
	CS_en3	|	CS_en2	|	CS_en1	|				|			|
	--------|-----------|-----------|---------------|-----------|-----------
	0		|	0		|	1		|	ADS5294		|	1		|	1
	--------|-----------|-----------|---------------|-----------|-----------
	0		|	1		|	0		|	DACX0508-1	|	1		|	0
	--------|-----------|-----------|---------------|-----------|-----------
	0		|	1		|	1		|ADG714+DACX0508|	1		|	0
	--------|-----------|-----------|---------------|-----------|-----------
	1		|	0		|	0		|	AD9508		|	1		|	1
	--------|-----------|-----------|---------------|-----------|-----------
	--------|-----------|-----------|---------------|-----------|-----------
	0		|	0		|	0		|	all CS are pulled high (default)

	-- CPOL -- 0表示空闲时SCLK为低电位；1表示空闲时SCLK为高电位
	-- CPHA -- 0表示从SCLK的第一个跳变沿开始采样；1表示从SCLK的第二个跳变沿开始采样

	在空闲状态下(A2,A1,A0)=(0,0,0)；当需要对某个芯片进行配置时，只需将(A2,A1,A0)设置
	成对应该芯片的一组值，在配置完成之后再恢复至(0,0,0)即可。

****************************************************************************/

#include "MephistoCAM.h"

#ifndef SPI_STATUS_DELAY
    #define SPI_STATUS_DELAY   1
#endif

#ifndef SPI_RST_DELAY
    #define SPI_RST_DELAY   2
#endif

//	设置SPI属性参数：
//	1）根据cpol, cpha配置SPI的极性和相位
//	2）通过A0,A1,A2配置片选信号
//	3）通过devname设置该SPI对应设备的名字
uint32_t setup_SPI( SPI_CONFIG * spi_cfg, 
                    uint32_t S1,
                    uint32_t S0,
				    uint32_t cpol, 
				    uint32_t cpha, 
				    uint32_t A2,
				    uint32_t A1,
				    uint32_t A0,
				    int status_bit_pos,
				    char *devname ){

    spi_cfg->S1     = S1;
    spi_cfg->S0     = S0;
	spi_cfg->cpol 	= cpol;
	spi_cfg->cpha 	= cpha;
	spi_cfg->A2		= A2;
	spi_cfg->A1		= A1;
	spi_cfg->A0		= A0;

	spi_cfg->status_bit_pos = status_bit_pos;

	sprintf(spi_cfg->devname, "%s", devname);

#if defined(_SPI_DEBUG_)
	print_spi_config( spi_cfg );
#endif

	return _SPI_SUCCESS_;
}

uint32_t setup_SPI_by_device_name(	SPI_CONFIG * spi_cfg, 
								    char *devname ){
	uint32_t status;
	uint32_t S1,S0;
	uint32_t cpol, cpha;
	uint32_t A2, A1, A0;
	int status_bit_pos;

	if( strcmp(devname, "ads5294") == 0 ){	//	ADS5294
	    S1      = 1;
	    S0      = 0;
	    
		cpol    = 1;
		cpha    = 1;
		
		A2      = 0;
		A1      = 0;
		A0      = 1;

		status_bit_pos = BIT_SPI_STATUS_ADS5294;
	} else if( strcmp(devname, "dacx0508") == 0 ){	//	DACx0508
	    S1      = 1;
	    S0      = 0;
	    
		cpol    = 1;
		cpha    = 0;
		
		A2      = 0;
		A1      = 1;
		A0      = 0;

		status_bit_pos = BIT_SPI_STATUS_DACX0508;
	} else if( strcmp(devname, "daisy") == 0 ){	//	Daisy
	    S1      = 1;
	    S0      = 0;
	    
		cpol    = 1;
		cpha    = 0;
		
		A2      = 0;
		A1      = 1;
		A0      = 1;

		status_bit_pos = BIT_SPI_STATUS_DAISY;
	} else if( strcmp(devname, "ad9508") == 0 ){	//	AD9508
	    S1      = 1;
	    S0      = 0;
	    
		cpol    = 1;
		cpha    = 1;
		
		A2      = 1;
		A1      = 0;
		A0      = 0;

		status_bit_pos = BIT_SPI_STATUS_AD9508;
	} else {
		char errmsg[1024];
		sprintf(errmsg, "Error: failed to configure SPI vai device name \'%s\' \n \
		                ==> devname must be one of : ads5294, dacx0508, daisy, ad9508\n", devname);
		printDebugInfo(errmsg);

		return _SPI_FAILURE_;
	}

	status = setup_SPI( spi_cfg, 
	                    S1, 
	                    S0, 
	                    cpol, 
	                    cpha, 
	                    A2, 
	                    A1, 
	                    A0,
	                    status_bit_pos,
	                    devname );

	return status;
}

uint32_t spi_data_alloc( SPI_DATA* spi_data, uint32_t data_size ){

	spi_data->size			= data_size;
	spi_data->reg_addr  	= malloc(sizeof(uint32_t)*data_size);
	spi_data->reg_val		= malloc(sizeof(uint32_t)*data_size);
	spi_data->reg_addr_val	= malloc(sizeof(uint32_t)*data_size);

	return _SPI_SUCCESS_;
}

uint32_t spi_data_free( SPI_DATA *spi_data ){

	if( spi_data->reg_val != NULL ){
		free(spi_data->reg_val);
	}

	if( spi_data->reg_addr != NULL ){
		free(spi_data->reg_addr);
	}

	if( spi_data->reg_addr_val != NULL ){
		free(spi_data->reg_addr_val);
	}
	
	free(spi_data);

	return _SPI_SUCCESS_;
}

void print_spi_config( SPI_CONFIG *cfg ){
	printf("\n> **** SPI CONFIG: ****\n");
	printf("> S1              = %d\n", cfg->S1);
	printf("> S0              = %d\n", cfg->S0);
	printf("> cpol            = %d\n", cfg->cpol);
	printf("> cpha            = %d\n", cfg->cpha);
	printf("> A2              = %d\n", cfg->A2);
	printf("> A1              = %d\n", cfg->A1);
	printf("> A0              = %d\n", cfg->A0);
	printf("> status_bit_post = %d\n", cfg->status_bit_pos);
	printf("> devname         = %s\n\n", cfg->devname);
}

//  旧版本,在拼接reg_addr_val时,地址向左移动固定的16位
uint32_t spi_data_set(  SPI_DATA *spi_data, 
                        uint32_t idx, 
                        uint32_t addr, 
                        uint32_t val ){
	
	spi_data->reg_addr[idx] 	= addr;
	spi_data->reg_val[idx]		= val;
	spi_data->reg_addr_val[idx]	= (addr << 16) + val;

	return _SPI_SUCCESS_;
}

//  不同芯片要求的SPI数据bits数不一定相同,因此需要分别实现相应的初始化函数,
//  这里增加一个可以根据需求指定左移位数的函数
//	LS = left shift
uint32_t spi_data_set_LS(   SPI_DATA *spi_data, 
                            uint32_t idx, 
                            uint32_t addr, 
                            uint32_t val, 
                            uint32_t LS_bit_num ){

	spi_data->reg_addr[idx] 	= addr;
	spi_data->reg_val[idx]		= val;
	spi_data->reg_addr_val[idx]	= (addr << LS_bit_num) + val;

	return _SPI_SUCCESS_;
}

//  实现单独一次完整的SPI数据传输
uint32_t send_spi_data_bits( uint32_t* gpio_reg, 
						     uint32_t* gpio2_reg, 
						     uint32_t* gpio_in_reg, 
						     SPI_CONFIG *spi_config,
						     uint32_t data ){

/*    printDebugInfo("> (A) pulling SPI CS high again!");*/

    //  SPI时钟的极性与相位设置
	set_SPI_CPOL(gpio_reg, spi_config->cpol);
	set_SPI_CPHA(gpio_reg, spi_config->cpha);
	
	//  SPI片选信号设置
	set_SPI_A2(gpio_reg, spi_config->A2);
	set_SPI_A1(gpio_reg, spi_config->A1);
	set_SPI_A0(gpio_reg, spi_config->A0);

	// step 1: prepare data, just write data into register pointed by gpio2_reg
	*gpio2_reg = data;  // {16bits-addr, 16bits-val}

/*	pull_down_SPI_CS(gpio_reg, spi_config);     // 片选信号为低电位有效*/
	pulse_SPI_RST(gpio_reg);    // trigger PL-SPI to work

	// step 3: wait finish signal from PL side
	int cnt=0;
	int cnt_max = 0xffff;
	
	uint32_t spi_status = get_bit(*gpio_in_reg, spi_config->status_bit_pos);

	while(1){

		if( spi_status == 1 ){
			return _SPI_SUCCESS_;
		}

	    usleep(SPI_STATUS_DELAY);
		
		spi_status = get_bit(*gpio_in_reg, spi_config->status_bit_pos);
		
		cnt++;

		if( cnt >= cnt_max ){
		    char errmsg[1024];
			sprintf(errmsg, "# WARNING @%s:%s:%d ==> SPI: cnt exceeds cnt_max = 0X%08X, failed to send data\n", 
			                __FILE__, 
			                __func__,
			                __LINE__,
			                cnt_max);
			printDebugInfo(errmsg);
/*			exit(0);*/
            break;
		}
	}

	//  SPI片选信号设置
	set_SPI_A2(gpio_reg, 0);
	set_SPI_A1(gpio_reg, 0);
	set_SPI_A0(gpio_reg, 0);

	// usleep(100);

	return _SPI_SUCCESS_;
}

//  Daisy-chain mode: 连续对同一个register发送4次数据
//  注意4-3-2-1的顺序
//  数据已经提前发送至PL端
uint32_t send_spi_data_bits_daisy_chain_mode(uint32_t* gpio_reg, 
					                         uint32_t* gpio2_reg, 
					                         uint32_t* gpio_in_reg, 
					                         SPI_CONFIG *spi_config){
    //  SPI时钟的极性与相位设置
	set_SPI_CPOL(gpio_reg, spi_config->cpol);
	set_SPI_CPHA(gpio_reg, spi_config->cpha);
	
	//  SPI片选信号设置
	set_SPI_A2(gpio_reg, spi_config->A2);
	set_SPI_A1(gpio_reg, spi_config->A1);
	set_SPI_A0(gpio_reg, spi_config->A0);

    //  配置DAISY模式
    set_SPI_DAISY( gpio_reg, 1 );	// 已经用不着了@2023-09-15

	pulse_SPI_RST( gpio_reg );    // trigger PL-SPI to work

	// step 3: wait finish signal from PL side
	int cnt=0;
	int cnt_max = 0xffff;

    uint32_t spi_status = get_bit(*gpio_in_reg, spi_config->status_bit_pos);

	while(1){

		if( spi_status == 1 ){
		    break;
		}

	    usleep(SPI_STATUS_DELAY);
		
		spi_status = get_bit(*gpio_in_reg, spi_config->status_bit_pos);
		
		cnt++;

		if( cnt >= cnt_max ){
		    char errmsg[1024];
			sprintf(errmsg, "# WARNING @%s:%s:%d ==> SPI: cnt exceeds cnt_max = 0X%08X, failed to send data\n", 
			                __FILE__, 
			                __func__,
			                __LINE__,
			                cnt_max);
			printDebugInfo(errmsg);

            break;
		}
	}
	
    //  配置DAISY模式
    set_SPI_DAISY( gpio_reg, 0 );

    return _SPI_SUCCESS_;                  
}

//  实现批量的等宽度的SPI数据传输
uint32_t send_spi_data( uint32_t* gpio_reg, 
					    uint32_t* gpio2_reg, 
					    uint32_t* gpio_in_reg, 
					    SPI_CONFIG *spi_config,
					    SPI_DATA *spi_data ){
    //  配置SPI传输数据的位数 (NOT USED ANYMORE...)
    // set_SPI_S1( gpio_reg, spi_config->S1 );
    // set_SPI_S0( gpio_reg, spi_config->S0 );
	
	uint32_t i;
	for( i=0; i<spi_data->size; i++ ){

		pull_up_SPI_CS( gpio_reg, spi_config );

		// printf("> (spi): i = %3d\n", i);
		int status = send_spi_data_bits(gpio_reg, 
										gpio2_reg,
										gpio_in_reg, 
										spi_config,
										spi_data->reg_addr_val[i] );
		
		if( _SPI_SUCCESS_ != status ){
			return _SPI_FAILURE_;
		}

		pull_up_SPI_CS( gpio_reg, spi_config );
		// usleep(1000);
	}

	pull_up_SPI_CS( gpio_reg, spi_config );

#ifdef _SPI_DEBUG_
	char mesg[1024];
	sprintf(mesg, "==> SPI: finished sending %4d {reg,val}\n", spi_data->size);
	printDebugInfo(mesg);
#endif

	return _SPI_SUCCESS_;
}

//	3个芯片组成菊花链构型
uint32_t send_spi_data_daisy_chain_mode( uint32_t* gpio_reg, 
					                     uint32_t* gpio2_reg, 
					                     uint32_t* gpio_in_reg, 
					                     SPI_CONFIG *spi_config,
					                     SPI_DATA *spi_data ){
    //  配置SPI传输数据的位数
    set_SPI_S1( gpio_reg, spi_config->S1 );
    set_SPI_S0( gpio_reg, spi_config->S0 );
    
    //  SPI时钟的极性与相位设置
	set_SPI_CPOL(gpio_reg, spi_config->cpol);
	set_SPI_CPHA(gpio_reg, spi_config->cpha);
	
	//  SPI片选信号设置
	set_SPI_A2(gpio_reg, spi_config->A2);
	set_SPI_A1(gpio_reg, spi_config->A1);
	set_SPI_A0(gpio_reg, spi_config->A0);
    
	uint32_t idx1, idx2, idx3;
	uint32_t spi_data1, spi_data2, spi_data3;

	uint32_t i;
	for( i=0; i<spi_data->size; i+=3 ){ // daisy-chain has 3 chips
	    
	    idx1 = i + 0;
	    idx2 = i + 1;
	    idx3 = i + 2;
	    
	    spi_data1 = spi_data->reg_addr_val[idx1];
	    spi_data2 = spi_data->reg_addr_val[idx2];
	    spi_data3 = spi_data->reg_addr_val[idx3];
	    
	    uint32_t spi_data_tmp[3];
	    
	    // 此处需要特别小心123的顺序!!!!!!
	    spi_data_tmp[0] = spi_data1;
	    spi_data_tmp[1] = spi_data2;
	    spi_data_tmp[2] = spi_data3;
    

	    // 先将数据发送至PL端的对应寄存器
	    SPI_PS_SEND_DATA(   gpio_reg,
                            gpio2_reg, 
                            spi_data_tmp,
                            3,
                            SPI_PS_DAISY );

	    // step 3: wait finish signal from PL side
	    int cnt=0;
	    int cnt_max = 0xffff;

	    while(1){
	    
		    uint32_t spi_status = get_bit(*gpio_in_reg, BIT_SPI_DAISY_CFG_DONE);

		    if( spi_status == 1 ){
			    break;
		    }

		    cnt++;
            usleep(SPI_STATUS_DELAY);

		    if( cnt >= cnt_max ){
	            char mesg[1024];
	            sprintf(mesg, "# WARNING : failed SPI_DAISY_MODE configure.");
	            printDebugInfo(mesg);
                break;
		    }
	    }
	    
        if( _SPI_SUCCESS_ != send_spi_data_bits_daisy_chain_mode(   gpio_reg, 
                                                                    gpio2_reg, 
                                                                    gpio_in_reg, 
                                                                    spi_config ) ){
            return _SPI_FAILURE_;
        }
	}
	
#ifdef _SPI_DEBUG_
	char mesg[1024];
	sprintf(mesg, "==> SPI: finished sending %4d {reg,val}\n", spi_data->size);
	printDebugInfo(mesg);
#endif

	return _SPI_SUCCESS_;
}

//  =============================================================
//  通过S1,S0配置SPI传输数据位数
void set_SPI_S1( uint32_t* gpio_reg, uint32_t S1 ){
    // if( S1 == 0 )
    //     clrbit(*gpio_reg, BIT_SPI_S1);
    
    // if( S1 == 1 )
    //     setbit(*gpio_reg, BIT_SPI_S1);
}

void set_SPI_S0( uint32_t* gpio_reg, uint32_t S0 ){
    // if( S0 == 0 )
    //     clrbit(*gpio_reg, BIT_SPI_S0);
    
    // if( S0 == 1 )
    //     setbit(*gpio_reg, BIT_SPI_S0);
}

//  =============================================================
//  SPI片选信号配置函数
void set_SPI_A2(uint32_t* gpio_reg, uint32_t A2){
	if( A2 == 0 )
		clrbit(*gpio_reg, BIT_SPI_A2);
	
	if( A2 == 1 )
		setbit(*gpio_reg, BIT_SPI_A2);
}

void set_SPI_A1(uint32_t* gpio_reg, uint32_t A1){
	if( A1 == 0 )
		clrbit(*gpio_reg, BIT_SPI_A1);
	
	if( A1 == 1 )
		setbit(*gpio_reg, BIT_SPI_A1);
}

void set_SPI_A0(uint32_t* gpio_reg, uint32_t A0){
	if( A0 == 0 )
		clrbit(*gpio_reg, BIT_SPI_A0);
	
	if( A0 == 1 )
		setbit(*gpio_reg, BIT_SPI_A0);
}

//  =============================================================
//  SPI时钟极性与相位配置函数
void set_SPI_CPOL(uint32_t* gpio_reg, uint32_t CPOL){
	if( CPOL == 0 )
		clrbit(*gpio_reg, BIT_SPI_CPOL);
	
	if( CPOL == 1 )
		setbit(*gpio_reg, BIT_SPI_CPOL);
}

void set_SPI_CPHA(uint32_t* gpio_reg, uint32_t CPHA){
	if( CPHA == 0 )
		clrbit(*gpio_reg, BIT_SPI_CPHA);
	
	if( CPHA == 1 )
		setbit(*gpio_reg, BIT_SPI_CPHA);
}

void set_SPI_DAISY( uint32_t* gpio_reg, uint32_t DAISY ){
    // if( DAISY == 0 )
    //     clrbit(*gpio_reg, BIT_SPI_DAISY_EN);

    // if( DAISY == 1 )
    //     setbit(*gpio_reg, BIT_SPI_DAISY_EN);
}

//  SPI数据传输结束之后,将片选信号拉高
void pull_up_SPI_CS( uint32_t* gpio_reg, SPI_CONFIG * spi_cfg ){
    
	clrbit(*gpio_reg, BIT_SPI_A2);
	clrbit(*gpio_reg, BIT_SPI_A1);
    clrbit(*gpio_reg, BIT_SPI_A0);
}

//  在SPI开始传输数据之前，需要将对应芯片的CS信号拉低
void pull_down_SPI_CS( uint32_t* gpio_reg, SPI_CONFIG * spi_cfg ){

//  首先确保CS信号处于高电位足够长的时间
	clrbit(*gpio_reg, BIT_SPI_A2);
	clrbit(*gpio_reg, BIT_SPI_A1);
	clrbit(*gpio_reg, BIT_SPI_A0);

	usleep(1000);   // 1000us = 1ms

//  根据芯片的配置来拉低CS信号	
    uint32_t cs = 0;
    set_SPI_A2( &cs, spi_cfg->A2 );
    set_SPI_A1( &cs, spi_cfg->A1 );
    set_SPI_A0( &cs, spi_cfg->A0 );
	
	*gpio_reg |= cs;
}

void pulse_SPI_RST(uint32_t* gpio_reg){
	clrbit(*gpio_reg, BIT_SPI_RST);
	delay(SPI_RST_DELAY);
	setbit(*gpio_reg, BIT_SPI_RST);
	delay(SPI_RST_DELAY);
	clrbit(*gpio_reg, BIT_SPI_RST);
}
